import * as THREE from 'three';
import gsap from 'gsap';

export class CameraUtils {
  /**
   * 初始化相机工具类
   * @param {THREE.PerspectiveCamera} camera - 相机实例
   * @param {MapControls} controls - 控制器实例
   * @param {THREE.Scene} scene - 场景实例（用于射线检测）
   * @param {Object} config - 配置参数
   */
  constructor (camera, controls, scene, config) {
    this.camera = camera;
    this.controls = controls;
    this.scene = scene; // 新增场景引用，用于射线检测
    this.minFov = 10;
    this.maxFov = 60;
    this.defaultHeightRatio = 1;
    this.lastMapInfo = null;
    this.tiltRatio = 0.5;
    this.targetTiltOffset = -0.05;
    this.focusPadding = 0.3;
    this.fov = null

    // 合并配置参数（聚焦与俯视图配置聚合）
    this.config = {
      tiltAngle: Math.PI / 7,
      distanceFactor: 4.0,
      heightOffset: 1.5,
      animationDuration: 1.6,
      topView: {
        polarAngle: 0.02,
        heightRatio: 1.2
      },
      ...config
    };

    // 初始化射线检测工具（封装到工具类内部）
    this.raycaster = new THREE.Raycaster();
    this.raycaster.params.Line.threshold = 0.8; // 线元素检测阈值
    this.mouse = new THREE.Vector2(); // 鼠标坐标
  }

  // 原有地图适配方法保持不变
  fitMapToViewMaximize (mapInfo, padding = 0.05) {
    if (!mapInfo || !mapInfo.Points.length) return;
    this.lastMapInfo = mapInfo;

    const { minX, maxX, minY, maxY, minZ, maxZ } = this.calculateBounds(mapInfo);
    const mapWidth = maxX - minX;
    const mapDepth = maxZ - minZ;
    const mapCenter = this.calculateCenter(minX, maxX, minY, maxY, minZ, maxZ);
    const mapMaxSize = Math.max(mapWidth, mapDepth);

    const mapAspect = mapWidth / mapDepth;
    const viewportAspect = this.camera.aspect;
    const paddingFactor = 1 + padding * 2;

    let targetDistance;
    if (mapAspect > viewportAspect) {
      targetDistance = (mapWidth * paddingFactor) / (2 * Math.tan(Math.PI * this.camera.fov / 360) * viewportAspect);
    } else {
      targetDistance = (mapDepth * paddingFactor) / (2 * Math.tan(Math.PI * this.camera.fov / 360));
    }

    const cameraZOffset = targetDistance * this.tiltRatio;
    this.camera.position.set(
      mapCenter.x,
      mapCenter.y + targetDistance * this.defaultHeightRatio,
      mapCenter.z - cameraZOffset
    );

    const targetZOffset = mapMaxSize * this.targetTiltOffset;
    const tiltedTarget = {
      x: mapCenter.x,
      y: mapCenter.y,
      z: mapCenter.z + targetZOffset
    };

    this.controls.target.set(tiltedTarget.x, tiltedTarget.y, tiltedTarget.z);
    this.controls.update();
    this.camera.updateProjectionMatrix();
  }

  // 原有方法保持不变...
  fitMapToView (mapInfo) {
    if (!mapInfo || (!mapInfo.Points.length && !mapInfo.Lines.length)) return;
    this.lastMapInfo = mapInfo;

    const { minX, maxX, minY, maxY, minZ, maxZ } = this.calculateBounds(mapInfo);
    const center = this.calculateCenter(minX, maxX, minY, maxY, minZ, maxZ);
    const distance = this.calculateDistance(minX, maxX, minY, maxY, minZ, maxZ);
    const mapMaxSize = Math.max(maxX - minX, maxZ - minZ);

    const cameraZOffset = distance * this.tiltRatio;
    this.camera.position.set(
      center.x,
      center.y + distance * this.defaultHeightRatio,
      center.z - cameraZOffset
    );

    const targetZOffset = mapMaxSize * this.targetTiltOffset;
    const tiltedTarget = {
      x: center.x,
      y: center.y,
      z: center.z + targetZOffset
    };

    this.controls.target.set(tiltedTarget.x, tiltedTarget.y, tiltedTarget.z);
    this.controls.update();
    this.camera.updateProjectionMatrix();
  }

  calculateBounds (mapInfo) {
    let minX = Infinity, maxX = -Infinity;
    let minY = Infinity, maxY = -Infinity;
    let minZ = Infinity, maxZ = -Infinity;

    mapInfo.Points.forEach(point => {
      minX = Math.min(minX, point.x);
      maxX = Math.max(maxX, point.x);
      minY = Math.min(minY, point.y);
      maxY = Math.max(maxY, point.y);
      minZ = Math.min(minZ, point.z);
      maxZ = Math.max(maxZ, point.z);
    });

    return { minX, maxX, minY, maxY, minZ, maxZ };
  }

  calculateCenter (minX, maxX, minY, maxY, minZ, maxZ) {
    return {
      x: (minX + maxX) / 2,
      y: (minY + maxY) / 2,
      z: (minZ + maxZ) / 2
    };
  }

  calculateDistance (minX, maxX, minY, maxY, minZ, maxZ) {
    const sizeX = maxX - minX;
    const sizeY = maxY - minY;
    const sizeZ = maxZ - minZ;
    const maxSize = Math.max(sizeX, sizeY, sizeZ);
    return maxSize / (2 * Math.tan(Math.PI * 60 / 360));
  }

  setCameraPosition (center, distance) {
    this.camera.position.set(
      center.x,
      center.y + distance * this.defaultHeightRatio,
      center.z + distance
    );
  }

  updateControls (center) {
    if (this.controls) {
      this.controls.target.set(center.x, center.y, center.z);
      this.controls.update();
    }
  }


  handleResize (size) {
    this.camera.aspect = size.width / size.height;
    this.camera.updateProjectionMatrix();
  }

  refitMap () {
    if (this.lastMapInfo) {
      this.fitMapToViewMaximize(this.lastMapInfo, 0.05);
    }
  }

  setFov (fov) {
    const validFov = Math.max(this.minFov, Math.min(this.maxFov, fov));
    this.camera.fov = validFov;
    this.camera.updateProjectionMatrix();
    if (this.lastMapInfo) {
      this.fitMapToViewMaximize(this.lastMapInfo, 0.05);
    }
  }


  /**
   * 双击事件处理（封装射线检测与聚焦逻辑）
   * @param {MouseEvent} e - 双击事件对象
   * @param {THREE.WebGLRenderer} renderer - 渲染器实例
   */
  handleDoubleClick (e, renderer) {
    // 1. 转换鼠标坐标为Three.js归一化坐标
    this.convertMouseToNDC(e, renderer.domElement);
    // 2. 射线检测相交元素
    const intersects = this.pickIntersects();
    // 3. 聚焦到目标点
    if (intersects.length > 0) {
      this.animateFocusTo(intersects[0].point);
    }
  }

  /**
   * 转换鼠标坐标为归一化设备坐标（NDC）
   * @param {MouseEvent} e - 鼠标事件
   * @param {HTMLCanvasElement} domElement - 渲染器画布
   */
  convertMouseToNDC (e, domElement) {
    const rect = domElement.getBoundingClientRect();
    this.mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
    this.mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
  }

  /**
   * 执行射线检测
   * @returns {Array} 相交元素数组
   */
  pickIntersects () {
    this.raycaster.setFromCamera(this.mouse, this.camera);
    return this.raycaster.intersectObjects(this.scene.children, true);
  }

  /**
   * 聚焦到目标点动画
   * @param {THREE.Vector3} targetPoint - 目标点坐标
   */
  animateFocusTo (targetPoint) {
    // 获取目标尺寸（通用值）
    const targetSize = new THREE.Vector3(2, 2, 2);
    // 计算相机拉远距离
    const baseDistance = Math.max(targetSize.x, targetSize.z) * this.config.distanceFactor;

    // 计算聚焦后相机位置
    const cameraX = targetPoint.x + Math.sin(this.config.tiltAngle) * baseDistance;
    const cameraZ = targetPoint.z - Math.cos(this.config.tiltAngle) * baseDistance;
    const cameraY = targetPoint.y + baseDistance * this.config.heightOffset;
    const newCameraPos = new THREE.Vector3(cameraX, cameraY, cameraZ);

    // 微调目标点
    const focusedTarget = new THREE.Vector3(
      targetPoint.x,
      Math.max(targetPoint.y, 0.5),
      targetPoint.z
    );

    // 执行聚焦动画
    gsap.timeline({ defaults: { ease: "power2.inOut" } })
      .to(this.camera.position, {
        x: newCameraPos.x,
        y: newCameraPos.y,
        z: newCameraPos.z,
        duration: this.config.animationDuration
      })
      .to(this.controls.target, {
        x: focusedTarget.x,
        y: focusedTarget.y,
        z: focusedTarget.z,
        duration: this.config.animationDuration
      }, 0)
      .to(this.camera, {
        fov: Math.max(35, this.camera.fov - 5),
        duration: this.config.animationDuration,
        onUpdate: () => this.camera.updateProjectionMatrix()
      }, 0);
  }

  /**
   * 动画回归俯视图
   */
  animateToTopView () {
    if (!this.lastMapInfo || !this.lastMapInfo.Points.length) return;
    // 计算地图边界和中心
    const { minX, maxX, minY, maxY, minZ, maxZ } = this.calculateBounds(this.lastMapInfo);
    const mapCenter = this.calculateCenter(minX, maxX, minY, maxY, minZ, maxZ);
    // 计算俯视图相机位置
    const targetDistance = this.calculateDistance(minX, maxX, minY, maxY, minZ, maxZ);


    // 计算俯视图目标点
    const topViewTarget = new THREE.Vector3(
      mapCenter.x,
      mapCenter.y,
      mapCenter.z
    );
    const distance = this.calculateDistance(minX, maxX, minY, maxY, minZ, maxZ);
    const cameraZOffset = distance * this.tiltRatio;

    // 执行俯视图动画
    gsap.timeline({
      defaults: {
        duration: this.config.animationDuration,
        ease: "power2.inOut"
      }
    })
      .to(this.camera.position, {
        x: mapCenter.x,
        y: mapCenter.y + targetDistance * this.defaultHeightRatio,
        z: mapCenter.z - cameraZOffset,
        onUpdate: () => this.controls.update()
      })
      .to(this.controls.target, {
        x: topViewTarget.x,
        y: topViewTarget.y,
        z: topViewTarget.z - 1,
        onUpdate: () => this.controls.update()
      }, 0)
      .to(this.controls, {
        polarAngle: this.config.topView.polarAngle,
        onUpdate: () => this.controls.update()
      }, 0)
      .to(this.camera, {
        fov: 40,
        onUpdate: () => this.camera.updateProjectionMatrix()
      }, 0);
  }

} ``